import { PlateList, ThsPlate } from "../views/models/ThsRankData";
import HttpModule from "./HttpModule";
import FileModule from "./FileModule";
import LogModule from "./LogModule";
import MatchModule from "./MatchModule";
import TimeModule from "./TimeModule";
import SWPlateModule from "./SWPlateModule";
import {ApiFilePath, ApiType} from "../define/ApiDefine";
import DataUtil from "../util/DataUtil";
import { septdata } from "../proto/septdata";
const { ccclass, property } = cc._decorator;

var isSt = function (name) {
    let isMatch = (name.indexOf('st') == -1 && name.indexOf('ST') == -1);
    return !isMatch;
}

@ccclass
export default class TushareModule {

    public static _instance: TushareModule = null;

    public stockList: any = null;

    //同花顺一级行业
    public thsIndustryMap: any = {};

    //同花顺细分行业
    public thsSubtradeMap: any = {};

    //同花顺概念
    public thsConceptMap: any = {};

    //股票同花顺概念列表
    public stockThsMember: any = {};

    //同花顺板块对应成分股股票
    public thsPlateMember: any = {};

    public dailyMarket: any = {};

    public stockInfo: any = {};

    public timeModule: TimeModule = null;

    public matchModule: MatchModule = null;
    public plateModule: SWPlateModule = null;
    private _initThs: boolean = false;

    private _totalText: string = null;

    private mainZb: any = {};
    private mainZb2: any = {};

    static getInstance(): TushareModule {
        if (TushareModule._instance == null) {
            TushareModule._instance = new TushareModule();
        }
        return TushareModule._instance;
    }

    async initStockList() {
        this.stockInfo = {};
        let fileName = ApiFilePath.StockList + ".data";
        let stockListData: septdata.StockListData = null;
        if (FileModule.isFileExist(fileName)) {
            let buffer = FileModule.loadUint8Array(fileName);
            stockListData = septdata.StockListData.decode(buffer);
        } else {
            let params = {
                list_status: 'L',
            };
            let list = await HttpModule.postAsync({
                params: params,
                api_name: ApiType.StockList
            });
            let stockList = DataUtil.convertToArray<septdata.StockBasicInfo>(list);
            stockListData = new septdata.StockListData();
            stockListData.data = stockList;
            let buffer = septdata.StockListData.encode(stockListData).finish();
            FileModule.saveUint8Array(fileName, buffer);
        }
        this.stockList = stockListData.data;
        for (let index = 0; index < stockListData.data.length; index++) {
            const item = stockListData.data[index];
            this.stockInfo[item.ts_code] = item;
        }
    }
    async initDailyMarket() {
        let dayStr = this.timeModule.curTradeDate();
        let fileName = ApiFilePath.StockList + '_' + dayStr + ".data";
        let dailyInfoData: septdata.StockDailyInfoData = null;
        if (FileModule.isFileExist(fileName)) {
            let buffer = FileModule.loadUint8Array(fileName);
            dailyInfoData = septdata.StockDailyInfoData.decode(buffer);
        } else {
            let params = {
                trade_date: dayStr,
            };

            let list = await HttpModule.postAsync({
                api_name: ApiType.DailyBasic,
                params: params,
                fields: 'ts_code,trade_date,close,turnover_rate,turnover_rate_f,volume_ratio,pe,pe_ttm,pb,ps,ps_ttm,dv_ratio,total_mv,circ_mv,free_share,total_share',
            });

            let dailyInfo = DataUtil.convertToArray<septdata.StockDailyInfo>(list);
            dailyInfoData = new septdata.StockDailyInfoData();
            dailyInfoData.data = dailyInfo;
            let buffer = septdata.StockDailyInfoData.encode(dailyInfoData).finish();
            FileModule.saveUint8Array(fileName, buffer);
        }
        
        this.dailyMarket = {};
        for (let j = 0; j < dailyInfoData.data.length; j++) {
            const info = dailyInfoData.data[j];
            let ts_code = info.ts_code;
            if (this.stockInfo[ts_code]) {
                this.dailyMarket[ts_code] = info;
            }
        }
    }

    async initOwnerInfo() {
        let list = <[]>this.stockList;
        let nullCount = 0;
        for (let index = 0; index < list.length; index++) {
            const item: any = list[index];
            let list_date = parseInt(item.list_date);
            if (item.holder_nums == null) {
                if (list_date < 20220301) {
                    nullCount++;
                }
            }
        }

        if (nullCount == 0) return;

        let params = {
            start_date: '20220301',
        };
        let countInfo = await HttpModule.postAsync({
            api_name: ApiType.OwnerCount,
            params: params,
            fields: 'ts_code,ann_date,holder_nums',
        });

        for (let index = 0; index < list.length; index++) {
            const item: any = list[index];
            let ts_code = item.ts_code;
            for (let j = 0; j < countInfo.length; j++) {
                const info = countInfo[j];
                if (info.ts_code == ts_code) {
                    if (item.holder_nums) {
                        let ann_date = parseInt(info.ann_date);
                        if (item.ann_date > ann_date) {
                            break;
                        }
                    }
                    if (info.holder_nums != null) {
                        item.holder_nums = info.holder_nums;
                        item.ann_date = parseInt(info.ann_date);
                    }
                }
            }
        }

        FileModule.saveJson(ApiFilePath.StockList, list);
        nullCount = 0;
        for (let index = 0; index < list.length; index++) {
            const item: any = list[index];
            if (item.holder_nums == null) {
                nullCount++;
            }
        }
        for (let index = 0; index < list.length; index++) {
            const item: any = list[index];
            let list_date = parseInt(item.list_date);
            if (item.holder_nums == null && list_date < 20220301) {

                LogModule.log('item.holder_nums = null, code:' + item.ts_code);
                let params = {
                    ts_code: item.ts_code,
                    start_date: '20220301',
                };
                let countInfo = await HttpModule.postAsync({
                    api_name: ApiType.OwnerCount,
                    params: params,
                    fields: 'ts_code,ann_date,holder_nums',
                });

                for (let j = 0; j < countInfo.length; j++) {
                    const info = countInfo[j];
                    if (info.ts_code == item.ts_code) {
                        if (item.holder_nums) {
                            let ann_date = parseInt(info.ann_date);
                            if (item.ann_date > ann_date) {
                                continue;
                            }
                        }
                        if (info.holder_nums == null) {
                            continue;
                        }
                        item.holder_nums = info.holder_nums;
                        item.ann_date = parseInt(info.ann_date);
                        LogModule.log('name:' + item.name + ', count:' + item.holder_nums);
                        FileModule.saveJson(ApiFilePath.StockList, list);
                    }
                }
            }
        }
    }

    async initTop10Holders() {
        let list = this.stockList;
        for (let index = 0; index < list.length; index++) {
            const item: any = list[index];
            let ts_code = item.ts_code;
            let fileName = ApiFilePath.Top10Holders + '_' + ts_code;
            if (FileModule.isExistJson(fileName) == false) {
                let params = {
                    ts_code: ts_code,
                    start_date: '20200601',
                };
                LogModule.log('拉取十大股东:' + ts_code);
                let top10 = await HttpModule.postAsync({
                    api_name: ApiType.Top10Holders,
                    params: params,
                    fields: 'ts_code,ann_date,end_date,holder_name,hold_amount',
                });

                let top10_float = await HttpModule.postAsync({
                    api_name: ApiType.Top10FloatHolders,
                    params: params,
                    fields: 'ts_code,ann_date,end_date,holder_name,hold_amount',
                });

                let data = {
                    top10: top10,
                    top10_float: top10_float,
                    ts_code: ts_code,
                    name: this.stockInfo[ts_code].name,
                }

                await FileModule.saveJsonNoFormat(fileName, data);
            }
        }
    }

    async getDailyLimitList(trades) {
        let dailys = [];
        for (let index = 0; index < trades.length; index++) {
            const date = trades[index];
            let params = {
                trade_date: date,
            };
            let fileName = ApiFilePath.LimitList + '_d_' + date;
            let limitList: any = await FileModule.loadJsonAsync(fileName);
            if (limitList == null || limitList.length == 0) {
                limitList = await HttpModule.postAsync({
                    api_name: ApiType.LimitList,
                    params: params,
                    // fields: 'ts_code,ann_date,holder_nums',
                }); 
                if (limitList && limitList.length > 0) {
                    await FileModule.saveJsonAsync(fileName, limitList, 10);
                } else {
                    continue;
                }
            }
            
            let upCount = 0;
            let downCount = 0;
            let zhaCount = 0;
            let upWeight = 0;
            let limitTimes = 0;
            for (let index = 0; index < limitList.length; index++) {
                const item = limitList[index];
                let ts_code = item.ts_code;
                if (isSt(item.name)) {
                    continue;
                }

                let isNew = false;
                if (this.stockInfo[ts_code] == null) {
                    isNew = true;
                } else {
                    let listDate = this.stockInfo[ts_code].list_date;
                    if (parseInt(listDate.slice(0, 6)) >= 202208) {
                        let tradeCount = TimeModule.getInstance().calcTradeCount(listDate, date);
                        if(tradeCount == item.limit_times) {
                            isNew = true;
                        }
                    }
                }

                if (item.limit == 'U') {
                    if (item.limit_times > 0) {
                        upCount++;
                        upWeight += item.limit_times;
                    }
                    if (limitTimes < item.limit_times && isNew == false) {
                        limitTimes = item.limit_times;
                    }
                } else if (item.limit == 'D') {
                    downCount++;
                } else if (item.limit == 'Z') {
                    zhaCount++;
                }
            }

            dailys.push({date: date, upLimitCount: upCount, upLimitWeight: upWeight, limitTimes: limitTimes,downLimitCount: downCount, zhaCount: zhaCount});
        }
        return dailys;
    }

    async initDailyTopData() {
        let dayStr = this.timeModule.curTradeDate();
        let params = {
            trade_date: dayStr,
        };
        let limitList = await HttpModule.postAsync({
            api_name: ApiType.LimitList,
            params: params,
            // fields: 'ts_code,ann_date,holder_nums',
        });

        let upList = [];
        let downList = [];
        for (let index = 0; index < limitList.length; index++) {
            const item = limitList[index];
            if (isSt(item.name)) {
                continue;
            }
            if (item.limit == 'U') {
                upList.push(item);
            } else {
                downList.push(item);
            }
        }

        let dailyText = `涨停家数:${upList.length},跌停家数:${downList.length}\n`;

        let thsIndex = {};

        for (let index = 0; index < upList.length; index++) {
            const item = upList[index];
            let itemStr = `${item.ts_code} ${item.name} 振幅:${item.amp} 涨停时间:${item.first_time} 打开次数:${item.open_times}\n`;
            dailyText += itemStr;
            let upThsItem = this.stockThsMember[item.ts_code];
            if (upThsItem == null) {
                continue;
            }
            let array = upThsItem.concept;
            for (let index = 0; index < array.length; index++) {
                const item = array[index];
                if (thsIndex[item.ths_code] == null) {
                    thsIndex[item.ths_code] = 0;
                }
                thsIndex[item.ths_code]++;
            }
        }

        let upThsArray = [];
        for (const key in thsIndex) {
            if (Object.prototype.hasOwnProperty.call(thsIndex, key)) {
                const count = thsIndex[key];
                let map = this.thsConceptMap[key];
                if (map && count > 1) {
                    let name = map.name;
                    upThsArray.push({ name: name, count: count });
                }
            }
        }

        upThsArray.sort(function (a, b) {
            return b.count - a.count;
        });
        dailyText += '\n\n\n';
        dailyText += '同花顺概念:\n';
        for (let index = 0; index < upThsArray.length; index++) {
            const item = upThsArray[index];
            let itemStr = `${item.name} 涨停家数: ${item.count}\n`;
            dailyText += itemStr;
        }
        await FileModule.saveStringAsync(ApiFilePath.DailyData + '_' + dayStr, dailyText, 100);

        await FileModule.saveJsonAsync(ApiFilePath.LimitList + '_' + dayStr, limitList, 100);
    }

    analyseStocksPlate (stocks, keyType) {
        let thsIndexMap = {
            industry: {},
            subtrade: {},
            concept: {},
        };

        let plateResult = {};
        for (let index = 0; index < PlateList.length; index++) {
            const key = PlateList[index];
            var PlateMapKeyList = ['thsIndustryMap', 'thsSubtradeMap', 'thsConceptMap'];
            var PlateTypeList = ['行业', '细分', '概念'];
            let mapKey = PlateMapKeyList[index];
            let thsIndex = thsIndexMap[key];
            for (let index = 0; index < stocks.length; index++) {
                const stock = stocks[index];
                let upThsItem = this.stockThsMember[stock.ts_code];
                if (upThsItem == null) {
                    continue;
                }
                let array = upThsItem[key];
                for (let index = 0; index < array.length; index++) {
                    const item = array[index];
                    if (thsIndex[item.ths_code] == null) {
                        thsIndex[item.ths_code] = [];
                    }
                    thsIndex[item.ths_code].push(stock);
                }
            }
            let upThsArray = [];
            for (const code in thsIndex) {
                if (Object.prototype.hasOwnProperty.call(thsIndex, code)) {
                    const stocks = thsIndex[code];
                    let map = this[mapKey][code];
                    if (map && stocks.length > 0) {
                        let name = map.name;
                        let weight = 0;
                        let itemData = { name: name, ts_code: code, stocks: stocks, type: PlateTypeList[index]};
                        if (keyType != '') {
                            for (let index = 0; index < stocks.length; index++) {
                                const stock = stocks[index];
                                weight += stock[keyType];
                            }
                            itemData['weight'] = weight;
                        }
                        upThsArray.push(itemData);
                    }
                }
            }

            upThsArray.sort(function (a, b) {
                if (keyType != '') {
                    return b['weight'] - a['weight'];
                } else {
                    return b.stocks.length - a.stocks.length;
                }
            });
            plateResult[key] = upThsArray;
        }
        
        return plateResult;        
    }

    async getConceptDetail(ts_code) {
        let params = {
            ts_code: ts_code,
        };
        let detailList = await HttpModule.postAsync({
            api_name: ApiType.ConceptDetail,
            params: params,
            fields: 'id,concept_name,ts_code',
        });

        let details = [];
        let strDetail = '';
        for (let index = 0; index < detailList.length; index++) {
            const detail = detailList[index];
            if (detail.ts_code == ts_code) {
                details.push(detail.concept_name);
                strDetail += detail.concept_name + ',';
            }
        }
        LogModule.log('ts_code:' + ts_code + ', ' + strDetail + '\n');
        return details;
    }

    getStockInfo(ts_code) {
        for (let index = 0; index < this.stockList.length; index++) {
            const info = this.stockList[index];
            if (info.ts_code == ts_code) {
                return info;
            }
        }
        return null;
    }

    getStockDetail(ts_code) {
        let info = this.stockInfo[ts_code];
        let market = this.dailyMarket[ts_code];
        let plate = this.stockThsMember[ts_code];
        let detail = { ts_code: ts_code, name: info.name, info: info, market: market, plate: plate };
        return detail;
    }

    getStockRecentInfo(ts_code) {
        let info = this.stockInfo[ts_code];
        if (info == null) {
            return null;
        }
        let market = this.dailyMarket[ts_code];
        let detail = DataUtil.combineData(info, market);
        return detail;
    }

    async getTop10Holders(ts_code) {
        let fileName = ApiFilePath.Top10Holders + '_' + ts_code;
        if (FileModule.isExistJson(fileName) == false) {
            return null;
        }
        let top10Data: any = await FileModule.loadJsonAsync(fileName);
        return top10Data;
    }

    async getStockDaily(ts_code) {
        let fileName = ApiFilePath.StockDaily + '_' + ts_code;
        LogModule.log('拉取股票日线:' + ts_code);
        let curDate = TimeModule.getInstance().curTradeDate();
        let dailyList: any = await FileModule.loadJsonAsync(fileName);
        let listDate = this.stockInfo[ts_code].list_date;
        if (dailyList == null) {
            let params = {
                ts_code: ts_code,
                start_date: listDate,
            };
            dailyList = await HttpModule.postAsync({
                api_name: ApiType.StockDaily,
                params: params,
            });

            let stkLimitList: any = await HttpModule.postAsync({
                api_name: ApiType.StkLimit,
                params: params,
                fields: 'ts_code,trade_date,pre_close,up_limit,down_limit',
            });
            let stkLimit = {};
            for (let index = 0; index < stkLimitList.length; index++) {
                const item = stkLimitList[index];
                stkLimit[item.trade_date] = item;
            }

            for (let index = 0; index < dailyList.length; index++) {
                const stock = dailyList[index];
                let item = stkLimit[stock.trade_date];
                if (item == null) {
                    continue;
                }
                stock.pre_close_2 = item.pre_close;
                stock.up_limit = item.up_limit;
                stock.down_limit = item.down_limit;
            }
            await FileModule.saveJsonAsync(fileName, dailyList, 10);
        } else {
            let lastEndDate = dailyList[0].trade_date;
            if (lastEndDate !== curDate) {
                let startDate = TimeModule.getInstance().nextTradeDate(lastEndDate);
                let params = {
                    ts_code: ts_code,
                    start_date: startDate,
                };
                let daily2 = await HttpModule.postAsync({
                    api_name: ApiType.StockDaily,
                    params: params,
                });

                if (daily2 == null || daily2.length == 0) {
                    return dailyList;
                }

                let stkLimitList: any = await HttpModule.postAsync({
                    api_name: ApiType.StkLimit,
                    params: params,
                    fields: 'ts_code,trade_date,pre_close,up_limit,down_limit',
                });
                let stkLimit = {};
                for (let index = 0; index < stkLimitList.length; index++) {
                    const item = stkLimitList[index];
                    stkLimit[item.trade_date] = item;
                }

                for (let index = 0; index < daily2.length; index++) {
                    const stock = daily2[index];
                    let item = stkLimit[stock.trade_date];
                    if (item == null) {
                        continue;
                    }
                    stock.pre_close_2 = item.pre_close;
                    stock.up_limit = item.up_limit;
                    stock.down_limit = item.down_limit;
                }

                dailyList = daily2.concat(dailyList);
                await FileModule.saveJsonAsync(fileName, dailyList, 10);
            }
        }

        return dailyList;
    }

    async getStockKLine(ts_code) {
        let dailyList = await this.getStockDaily(ts_code);
        let listDate = this.stockInfo[ts_code].list_date;
        let monthList = [];
        let weekList = [];

        let startMonth = listDate.slice(0, 6);
        let startWeek = TimeModule.getInstance().getWeekCount(listDate, listDate);
        let monthLitter = [];
        let weekLitter = [];
        for (let index = dailyList.length - 1; index >= 0; index--) {
            const item = dailyList[index];
            let date = item.trade_date;
            let month = date.slice(0, 6);
            let weekIndex = TimeModule.getInstance().getWeekCount(listDate, date);
            if (month == startMonth) {
                monthLitter.push(item);
            } else {
                let data = this.combineDaily(ts_code, startMonth, monthLitter);
                monthList.push(data);
                monthLitter = [item];
                startMonth = month;
            }
            if (startWeek == weekIndex) {
                weekLitter.push(item);
            } else {
                let data = this.combineDaily(ts_code, date, weekLitter);
                weekList.push(data);
                monthLitter = [item];
                startWeek = weekIndex;
            }
        }
        monthList.push(this.combineDaily(ts_code, startMonth, monthLitter));
        weekList.push(this.combineDaily(ts_code, TimeModule.getInstance().curTradeDate(), weekLitter));

        let kline = {dailyList: dailyList.reverse(), monthList: monthList, weekList: weekList};
        return kline;
    }

    combineDaily (ts_code, date, litterArray) {
        let open_qfq = litterArray[0].open_qfq;
        let open = litterArray[0].open;
        let pre_close_qfq = litterArray[0].pre_close_qfq;
        let pre_close = litterArray[0].pre_close;
        let close_qfq = litterArray[litterArray.length - 1].close_qfq;
        let close = litterArray[litterArray.length - 1].close;
        let low_qfq = litterArray[0].low_qfq;
        let low = litterArray[0].low;
        let high_qfq = 0;
        let high = 0;
        let amount = 0;
        for (let index = 0; index < litterArray.length; index++) {
            const item = litterArray[index];
            if (item.high > high) {
                high = item.high;
                high_qfq = item.high_qfq;
            }
            if (item.low < low) {
                low = item.low;
                low_qfq = item.low_qfq;
            }
            amount += item.amount;
        }

        let data = {
            ts_code: ts_code,
            trade_date: date,
            open_qfq: open_qfq,
            open: open,
            pre_close_qfq: pre_close_qfq,
            pre_close: pre_close,
            close_qfq: close_qfq,
            close: close,
            low_qfq: low_qfq,
            low: low,
            high_qfq: high_qfq,
            high: high,
            amount: amount,
        };
        return data;
    }

    async getTotalStockDaily(trade_date) {
        let fileName = ApiFilePath.StockDaily + '_all_' + trade_date;
        LogModule.log('拉取股票日线:' + trade_date);
        let dailyList: any = await FileModule.loadJsonAsync(fileName);
        if (dailyList == null || dailyList.length == 0) {
            let params = {
                trade_date: trade_date,
            };
            dailyList = await HttpModule.postAsync({
                api_name: ApiType.StockDaily,
                params: params,
            });

            if (dailyList == null || dailyList.length == 0) {
                return [];
            }
            let stkLimitList: any = await HttpModule.postAsync({
                api_name: ApiType.StkLimit,
                params: params,
                fields: 'ts_code,trade_date,pre_close,up_limit,down_limit',
            });
            let stkLimit = {};
            for (let index = 0; index < stkLimitList.length; index++) {
                const item = stkLimitList[index];
                stkLimit[item.ts_code] = item;
            }

            for (let index = 0; index < dailyList.length; index++) {
                const stock = dailyList[index];
                let item = stkLimit[stock.ts_code];
                if (item == null) {
                    continue;
                }
                stock.pre_close_2 = item.pre_close;
                stock.up_limit = item.up_limit;
                stock.down_limit = item.down_limit;
            }

            await FileModule.saveJsonAsync(fileName, dailyList, 200);
        }
        return dailyList;
    }

    isZhuBan(ts_code) {
        let front = ts_code.slice(0, 2);
        if (front == '30' || front == '68') {
            return false;
        }
        if (ts_code.slice(0, 1) == '8') {
            return false;
        }
        return true;
    }
    async initDate() {
        LogModule.log('正在拉取日期数据');
        let strToday = TimeModule.getInstance().strToday();
        let fileName = ApiFilePath.TradeCal + '_' + strToday;
        let dates: any = await FileModule.loadJsonAsync(fileName);
        if (dates == null) {
            let params = {
                exchange: 'SSE',
                start_date: '20000101',
                end_date: strToday,
                is_open: '1',
            };

            let list = await HttpModule.postAsync({
                params: params,
                api_name: ApiType.TradeCal
            });

            dates = [];
            for (let index = 0; index < list.length; index++) {
                const item = list[index];
                dates.push(item.cal_date);
            }

            FileModule.saveJson(fileName, dates);
        }
        return dates;
    }

    //分析公司主营业务 盈利 净利润
    async getAllMainBz () {
        this.mainZb = {};
        this.mainZb2 = {};

        for (let index = 0; index < this.stockList.length; index++) {
            const info = this.stockList[index];
            let params = {
                start_date: '20100101',
                ts_code: info.ts_code,
                // end_type: 4,
                report_type: 1,
            };

            let ts_code = info.ts_code;
            if (ts_code.slice(0,3) == '688') {
                continue;
            }
            if (ts_code.slice(0,1) == '8') {
                continue;
            }
            let itemIncome: any = await FileModule.loadJsonAsync(ApiFilePath.Income + '_' + info.ts_code);
            if (itemIncome) {
                this.mainZb[ts_code] = itemIncome;
                this.mainZb2[ts_code] = {};
                var items = this.mainZb2[ts_code];
                for (let index = 0; index < itemIncome.length; index++) {
                    const detail = itemIncome[index];
                    if (items[detail.end_date] == null) {
                        items[detail.end_date] = detail;
                    }   
                }
                continue;
            }

            itemIncome = await HttpModule.postAsync({
                api_name: ApiType.Income,
                params: params,
                fields: 'ts_code,ann_date,end_date,total_revenue,n_income',
            });

            for (let index = 0; index < itemIncome.length; index++) {
                const detail = itemIncome[index];
                detail.revenueStr = Math.floor(detail.total_revenue / 1000) + '万元';
                detail.incomeStr = Math.floor(detail.n_income / 1000) + '万元';
                let ts_code = detail.ts_code;
                if (this.mainZb[ts_code] == null) {
                    this.mainZb[ts_code] = [];
                    this.mainZb2[ts_code] = {};
                }
                var items = this.mainZb2[ts_code];
                let length = this.mainZb[ts_code].length;
                if (length > 0) {
                    let lastItem = this.mainZb[ts_code][length - 1];
                    if (lastItem.ann_date != detail.ann_date) {
                        this.mainZb[ts_code].push(detail);
                        items[detail.end_date] = detail;
                    } else {
                        if (detail.update_flag) {
                            this.mainZb[ts_code][length - 1] = detail;
                            items[detail.end_date] = detail;
                        }
                    }
                }
            }
            await FileModule.saveJsonAsync(ApiFilePath.Income + '_' + info.ts_code, itemIncome, 100);
        }
        
        return this.mainZb;        
    }

    //分析公司主营业务 盈利 净利润
    async analyseMainBz () {
        LogModule.log("开始拉取主营业务数据");
        let mainBzList: any = await this.getAllMainBz();
        LogModule.log("主营业务数据拉去完成");
        let bestComp = [];
        let totalStr = "";
        for (const key in mainBzList) {
            if (Object.prototype.hasOwnProperty.call(mainBzList, key)) {
                const mainBz = [];
                let array = mainBzList[key];
                for (let index = 0; index < array.length; index++) {
                    const item = array[index];
                    if (item.end_date.indexOf('1231') != -1) {
                        mainBz.push(item);
                    }
                }
                if (mainBz.length < 4) continue;

                let newIncomes = [];
                let lastIncome = null;
                for (let index = 0; index < mainBz.length; index++) {
                    const item = mainBz[index];
                    if (lastIncome && lastIncome.end_date == item.end_date) {
                        continue;
                    }
                    newIncomes.push(item);
                    lastIncome = item;
                }

                if (newIncomes.length > 3) {
                    newIncomes = newIncomes.slice(0, 3);
                }

                let ts_code = newIncomes[0].ts_code;
                let info: any = this.getStockInfo(ts_code);
                let listDate = parseInt(info.list_date);
                if (listDate > 20190000) {
                    continue;
                }
                let market = this.dailyMarket[ts_code];
                if (market) {
                    if (market.total_mv > 1500000) {
                        continue;
                    }
                }

                let upSaleCount = 0;
                let totalSaleRate = 0;
                let upIncomeCount = 0;
                let totalIncomeRate = 0;
                for (let index = newIncomes.length - 1; index > 0; index--) {
                    let item = newIncomes[index - 1];
                    let item2 = newIncomes[index];
                    //计算营业收入增长率
                    item.salesRate = item.total_revenue / item2.total_revenue - 1;
                    totalSaleRate += item.salesRate;
                    if (item.salesRate < 0.1) {
                        upSaleCount++;
                    }

                    //计算净利润增长
                    let incomeRate = Math.abs(item.n_income / item2.n_income) - 1;
                    if (item.n_income < 0) {
                        incomeRate = -Math.abs(item.n_income / item2.n_income) - 1;
                        totalSaleRate = -1;
                        break;
                    }
                    totalIncomeRate += incomeRate;
                    item.incomeRate = incomeRate;
                    if (item.incomeRate < 0.1) {
                        upIncomeCount++;
                    }
                }
                let saleRate = totalSaleRate / (newIncomes.length - 1);
                let incomeRate = totalIncomeRate / (newIncomes.length - 1);
                if ((upIncomeCount + upSaleCount) / (newIncomes.length - 1) > 0.3) {
                    continue;
                }
                if (saleRate > 0 && incomeRate > 0.2) {
                    let tatalRate = newIncomes[0].total_revenue / newIncomes[newIncomes.length - 1].total_revenue;
                    let tatalIncomeRate = newIncomes[0].n_income / newIncomes[newIncomes.length - 1].n_income;
                    let rate2 =  Math.pow(1.2, newIncomes.length - 1);
                    let items = this.mainZb2[ts_code];
                    let income1 = items['20230630'];
                    let income2 = items['20220630'];
                    let saleRate2 = income1.total_revenue / income2.total_revenue - 1;
                    let incomeRate2 = income1.n_income / income2.n_income - 1;
                    if (income1.n_income < 0 || saleRate2 < 0 || incomeRate2 < 0.2) {
                        continue;
                    }
                    if (tatalRate + tatalIncomeRate > rate2 * 2) {
                        let market = this.dailyMarket[ts_code];
                        if (market) {
                            if (market.pe && market.pe > 0 && market.pe_ttm > 0 && market.pe_ttm < 60) {
                                if (info) {
                                    info.incomes = newIncomes;
                                    info.saleRate = saleRate;
                                    info.incomeRate = incomeRate;
                                    let obj = {
                                        info: info,
                                        market: market,
                                    };
                                    bestComp.push(obj);
                                    LogModule.log(`ts_code:${ts_code},name:${info.name},saleRate:${saleRate},incomeRate:${incomeRate},pb:${market.pe},pbttm:${market.pe_ttm}`,);

                                    // 营业额增长率:${saleRate} 净利润增长率:${incomeRate} 市盈率:${market.pe_ttm} 市净率:${market.pe} 总市值:${Math.ceil(market.total_mv / 10000)}亿 
                                    totalStr += `${ts_code} ${info.name}\n`;

                                }
                            }
                        }
                    }
                }
            }
        }
        LogModule.log('bestComp count:' + bestComp.length);
        await FileModule.saveStringAsync(ApiFilePath.BestCompany, totalStr, 10);
        await FileModule.saveJsonAsync(ApiFilePath.BestCompany, bestComp, 100);
    }

    async initModuleData() {
        if (this._initThs == false) {
            LogModule.log('正在下载股票列表');
            await this.initStockList();
            LogModule.log('正在拉取股票基本信息:' + this.timeModule.curTradeDate());
            await this.initDailyMarket();
            LogModule.log('正在拉取股东数量');
            // await this.initOwnerInfo();
            // LogModule.log('正在拉取十大股东');
            // await this.initTop10Holders();
            // LogModule.log('下载基础数据完成');
            this._initThs = true;
        }
    }

    async initModule() {
        this.timeModule = TimeModule.getInstance();
        this.matchModule = new MatchModule();
        this.plateModule = SWPlateModule.getInstance();
        let dates = await this.initDate();
        this.timeModule.initDates(dates);
        this.plateModule.initModule();
        await this.initModuleData();
    };
}